package cn.ex.ymcc.service.impl;

import cn.ex.ymcc.domain.Course;
import cn.ex.ymcc.domain.CourseMarket;
import cn.ex.ymcc.domain.CourseOrder;
import cn.ex.ymcc.domain.CourseOrderItem;
import cn.ex.ymcc.dto.Course2OrderParams;
import cn.ex.ymcc.dto.KillCourseOrderDto;
import cn.ex.ymcc.dto.KillPreOrderParamsDto;
import cn.ex.ymcc.dto.PayDto;
import cn.ex.ymcc.feign.CourseUserLearnFeignClient;
import cn.ex.ymcc.globalexception.GlobalException;
import cn.ex.ymcc.mapper.CourseOrderMapper;
import cn.ex.ymcc.result.JSONResult;
import cn.ex.ymcc.service.ICourseOrderItemService;
import cn.ex.ymcc.service.ICourseOrderService;
import cn.ex.ymcc.util.AssertUtil;
import cn.ex.ymcc.util.CodeGenerateUtils;
import cn.ex.ymcc.vo.CourseItemVo;
import cn.ex.ymcc.vo.CourserOrderVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author aze
 * @since 2022-08-20
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private CourseUserLearnFeignClient courseInfoForOrderFeignClient;
    @Autowired
    private ICourseOrderItemService courseOrderItemService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private RedissonClient redissonClient;
    /**
     *
     * 下单
     * @param dto
     * @return
     */
    @Override
    public String placeOrder(Course2OrderParams dto) {
        Long loginId = 1L;
        //多个课程id以逗号分割
        String courseIdsStr = StringUtils.join(dto.getCourseIds(), ",");
        //courseId + loginId
        String key = courseIdsStr+":"+loginId;
        //校验防重token
        Object tokenTmp = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(tokenTmp,"token失效，请重新下单！");
        AssertUtil.isEquals(dto.getToken(),tokenTmp.toString(),"token错误，请重新下单");
        //查询课程
        JSONResult jsonResult = courseInfoForOrderFeignClient.courseOrderInfo(courseIdsStr);
        AssertUtil.isTrue(jsonResult.isSuccess(),"信息错误，下单失败！");
        AssertUtil.isNotNull(jsonResult.getData(),"服务不可达，下单失败！");
        String objStr = JSON.toJSONString(jsonResult.getData());
        CourserOrderVo orderVo = JSONObject.parseObject(objStr,CourserOrderVo.class);
        List<CourseItemVo> courseInfos = orderVo.getCourseInfos();
        AssertUtil.isNotNull(courseInfos,"课程信息有误，下单失败");
        //保存订单
        Date date = new Date();
        CourseOrder order = new CourseOrder();
        order.setCreateTime(date);
        order.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        order.setStatusOrder(CourseOrder.STATE_WAITE_PAY);
        order.setUserId(loginId);
        order.setVersion(0);
        order.setPayType(dto.getPayType());
        StringBuilder sb = new StringBuilder();
        sb.append("课程【");
        courseInfos.forEach(courseInfoVo->{
            Course course = courseInfoVo.getCourse();
            CourseMarket courseMarket = courseInfoVo.getCourseMarket();
            //子订单
            CourseOrderItem item = new CourseOrderItem();
            item.setAmount(courseMarket.getPrice());
            item.setCount(1);

            item.setCreateTime(date);
            item.setCourseId(course.getId());
            item.setCourseName(course.getName());
            item.setCoursePic(course.getPic());
            item.setVersion(order.getVersion());
            item.setOrderNo(order.getOrderNo());
            order.setTotalCount(order.getTotalCount() + 1);
            //设置金额
            order.setTotalAmount(order.getTotalAmount().add(item.getAmount()));
            sb.append(course.getName()).append(",");
            order.getItems().add(item);//
        });
        sb.append("】订单");
        //修改主订单标题
        order.setTitle(sb.toString());
        //发送mq事务消息
        Map<Object, Object> extParam = new HashMap<>();
        extParam.put("userId",loginId);
        extParam.put("courseId",courseIdsStr);
        PayDto payDto = new PayDto(
                order.getTotalAmount(),
                order.getPayType(),
                null,
                order.getOrderNo(),
                order.getUserId(),
                JSON.toJSONString(extParam),
                order.getTitle()
        );

        //构建消息体  JSON.toJSONString(payDto) 转为json string 发送
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        //发送事务消息
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "service-order-producer",//生产者消息组名
                "course-order-topic:course-order-tag", //topic + tags
                message,  //消息体
                order   //额外参数
        );
        log.info("发送完事务消息--检查事务消息状态");
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        SendStatus sendStatus = transactionSendResult.getSendStatus();
        //校验事务状态和消息状态
        if(localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            log.info("校验发送事务消息状态异常");
            throw new GlobalException("下单失败！！");
        }
        //删除token
        redisTemplate.delete(key);
        return order.getOrderNo();
    }
    //保存主订单和子订单
    @Override
    public void saveOrderAndItems(CourseOrder courseOrder) {
        CourseOrder orderTmp = selectOrderByOrderNo(courseOrder.getOrderNo());
        AssertUtil.isNull(orderTmp,"订单已经存在，下单失败");
        //保存订单
        insert(courseOrder);//保存主订单
        //保存子订单
        courseOrder.getItems().forEach(item->{
            item.setOrderId(courseOrder.getId());
            courseOrderItemService.insert(item);
        });
    }

    /**
     * 查询课程订单是否存在
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrder selectOrderByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }

    /**
     *支付成功，修改订单状态
     * @param courseOrder
     */
    @Override
    public void payOrder(CourseOrder courseOrder) {
        courseOrder.setStatusOrder(CourseOrder.STATE_PAY_SUCCESS);
        courseOrder.setUpdateTime(new Date());
        updateById(courseOrder);
    }

    /**
     * 支付超时，取消订单
     * @param orderNo
     */
    @Override
    public void cancelOrder(String orderNo) {

        CourseOrder courseOrder = selectOrderByOrderNo(orderNo);
        AssertUtil.isNotNull(courseOrder,"订单不存在");
        if(courseOrder.getStatusOrder().intValue() != CourseOrder.STATE_PAY_SUCCESS.intValue()){
            courseOrder.setStatusOrder(CourseOrder.STATE_CANCEL);
            updateById(courseOrder);
        }
    }

    /**
     * 秒杀下单
     * @param dto
     * @return
     */
    @Override
    public String killPlaceOrder(KillCourseOrderDto dto) {
        //获取预创订单
        KillPreOrderParamsDto preOrder = (KillPreOrderParamsDto) redisTemplate.opsForValue().get(dto.getOrderNo());
        AssertUtil.isNotNull(preOrder,"秒杀非法！！");

        //校验token是否有效
        Long loginId = 1L;
        String key = preOrder.getCourseId()+":"+loginId;
        Object tokenTmp = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(tokenTmp,"请重新下单！");
        AssertUtil.isEquals(dto.getToken(),tokenTmp.toString(),"请重新下单");
        /*
        保存订单
         */
        Date date = new Date();
        CourseOrder order = new CourseOrder();
        order.setCreateTime(date);
        order.setOrderNo(preOrder.getOrderNo());
        order.setStatusOrder(0);
        order.setUserId(preOrder.getUserId());
        order.setVersion(0);
        order.setPayType(dto.getPayType());
        order.setTotalAmount(preOrder.getKillAmount());
        order.setTotalCount(preOrder.getKillCount());
        order.setTitle("秒杀下单："+preOrder.getCourseName());
        //默认秒杀一件商品
        //保存子订单
        CourseOrderItem item = new CourseOrderItem();

        item.setAmount(preOrder.getKillAmount());
        item.setCount(preOrder.getKillCount());
        item.setCreateTime(date);
        item.setCourseId(preOrder.getCourseId());
        item.setCourseName(preOrder.getCourseName());
        item.setCoursePic(preOrder.getCoursePic());
        item.setVersion(order.getVersion());
        item.setOrderNo(order.getOrderNo());
        //在主订单中添加子订单
        order.getItems().add(item);

        Map<Object, Object> extParam = new HashMap<>();
        extParam.put("userId",loginId);
        extParam.put("courseId",preOrder.getCourseId());
        PayDto payDto = new PayDto(
                order.getTotalAmount(),
                order.getPayType(),
                null,
                order.getOrderNo(),
                order.getUserId(),
                JSON.toJSONString(extParam),
                order.getTitle()
        );

        //发送事务消息 构建消息体
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "service-order-producer",//要和监听器上注解申明的名字一致
                "course-order-topic:course-order-tag",
                message,  //消息体
                order   //额外参数
        );

        //校验事务状态，和消息发送状态
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        SendStatus sendStatus1 = transactionSendResult.getSendStatus();
        if(localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus1 != SendStatus.SEND_OK ){
            log.info("校验发送事务消息状态异常");
            throw new GlobalException("下单失败！！");
        }
        //发布支付超时延迟消息
        SendResult sendResult;
        SendStatus sendStatus2;
        try {
                    sendResult = rocketMQTemplate.syncSend(
                    "paytimeout-topic:paytimeout-tag",//topic:tag
                    MessageBuilder.withPayload(order.getOrderNo()).build(),//消息体
                    2000,//发送延迟消息的超时时间
                    4//延迟等级
            );
            sendStatus2 = sendResult.getSendStatus();
            if(sendStatus2 != SendStatus.SEND_OK){
                //重发三次不成功则通知管理人员
                int j = 3;
                for(int i = j;i<=j;i--) {
                    sendResult = rocketMQTemplate.syncSend(
                            "paytimeout-topic:paytimeout-tag",//topic:tag
                            MessageBuilder.withPayload(order.getOrderNo()).build(),//消息体
                            2000,//发送延迟消息的超时时间
                            4//延迟等级
                    );

                    if(sendStatus2 == SendStatus.SEND_OK) break;

                    if(i <= 0) break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        redisTemplate.delete(key);
        //返回订单号
        return order.getOrderNo();
    }
}
